package de.szut.dqi10.brailletouchscreenreader.textprocessing;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;

import de.szut.dqi10.brailletouchscreenreader.braillechar.BrailleChar;
import de.szut.dqi10.brailletouchscreenreader.braillechar.SixPointBrailleChar;

/**
 * Class that allows for conversion from plain text to braille
 * @author Marco Jedzig, Simon Grafenhorst, Felix Pusch
 */
public class TextToBrailleConverter {
	private static final String TAG = "TextToBrailleConverter";
	private static final Pattern PATTERN_NUMBER = Pattern.compile("(\\d+)");
	
	private HashMap<String,Byte> alphabet;
	
	/**
	 * @return the alphabet
	 */
	public HashMap<String, Byte> getAlphabet() {
		return alphabet;
	}
	/**
	 * @param alphabet the alphabet to set
	 */
	public void setAlphabet(HashMap<String, Byte> alphabet) {
		this.alphabet = alphabet;
	}
	
	/**
	 * Converts an entire String to a LinkedList of BrailleChar(s)
	 * An alphabet has to bet set via setAlphabet() before this method can work.
	 * @param inputString the string to convert
	 * @return the converted inputString in a LinkedList of BrailleChar representation
	 */
	public LinkedList<BrailleChar> getStringAsBraille(String inputString) {
		LinkedList<BrailleChar> characters = new LinkedList<BrailleChar>();
		//need to convert to lower case, since upper case not needed
		//use Default Locale
		inputString = inputString.toLowerCase(Locale.getDefault());
		
		inputString = stripInvalidCharacters(inputString);
		
		inputString = fixNumbers(inputString);
		
		String[] words = inputString.split(" ");
		for (String word : words) { //for each word
			if (word.length() > 0) {
				//Check entire word
				boolean done = false;
				String exp;
				int start = 0;
				int end = word.length();
				while(!done) {
					exp = word.substring(start, end);
					
					if (alphabet.containsKey(exp)) {
						Log.i(TAG, "Found character: " + exp);
						characters.add(new SixPointBrailleChar(alphabet.get(exp)));
						start = end;
						end = word.length();
						if (end == start) {
							done = true;
						}
					} else {
						//Check with one character less
						end--;
					}
				}
				characters.add(new SixPointBrailleChar((byte)0));
			}
		}
		return characters;
	}
	
	/**
	 * Modify the input String so that numbers are containing using the "#number " format.
	 * @param input the input String
	 * @return the modified String
	 */
	private String fixNumbers(String input) {
		Log.i(TAG, "Fixing numbers in: " + input);
		Matcher m = PATTERN_NUMBER.matcher(input); //match input with numberPattern
		String result = new String(); //need to create new String since java.lang.String is immutable
		int lastFind = 0;
		while(m.find()) { //Aslong as there is a group of numbers
			result += input.substring(lastFind, m.start()); //Add all characters pre-number
			lastFind = m.end(); 
			String number = m.group(); //Get the matcher group
			result += "#" + number + " "; //Add # and whitespace 
			Log.i(TAG, "Found number group: " + number);
		}
		result += input.substring(lastFind, input.length()); //Add trailing characters
		result = result.trim(); //Trim so no double whitespace occurs later
		Log.i(TAG, "Done fixing numbers: " + result);
		return result;
	}
	
	/**
	 * Removes all Characters that do not have a mapping in the loaded Braille alphabet
	 * @param input the String to strip
	 * @return the stripped String
	 */
	private String stripInvalidCharacters(String input) {
		StringBuilder s = new StringBuilder();
		s.append("[^"); //Start character class
		for (Entry<String, Byte> e : alphabet.entrySet()) {
			s.append(Pattern.quote(e.getKey())); //"Auto"escapes the character
		}
		s.append(" "); // Add Space as valid character
		s.append("]"); //Close character class
		input = input.replaceAll(s.toString(), "");
		input = input.trim();
		return input;
	}
}